package com.biz.primus.account.config;

import com.biz.primus.account.bean.CasAuthenticationRequestMatcher;
import com.biz.primus.account.security.HttpSecurityConfig;
import com.biz.primus.account.security.PrimusCasAuthenticationEntryPoint;
import com.biz.primus.account.security.PrimusCasAuthenticationFilter;
import com.biz.primus.account.security.PrimusCasAuthenticationProvider;
import com.biz.primus.account.service.PrimusUserDetailService;
import com.biz.primus.account.utils.SSOUtils;
import java.io.IOException;
import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import org.jasig.cas.client.session.SingleSignOutFilter;
import org.jasig.cas.client.validation.Cas20ServiceTicketValidator;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.cas.ServiceProperties;
import org.springframework.security.cas.authentication.CasAssertionAuthenticationToken;
import org.springframework.security.cas.web.CasAuthenticationFilter;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.core.userdetails.AuthenticationUserDetailsService;
import org.springframework.security.web.authentication.logout.LogoutFilter;
import org.springframework.security.web.authentication.logout.SecurityContextLogoutHandler;
import static com.biz.primus.account.config.CasConfig.*;

/**
 * Created by younger on 17/11/2.
 */
@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true, jsr250Enabled = true, securedEnabled = true)
public class WebSecurityConfig extends WebSecurityConfigurerAdapter{

    @Autowired(required = false)
    private HttpSecurityConfig httpSecurityConfig;

    @Autowired
    private CasConfig casConfig;

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        if(httpSecurityConfig != null){
            httpSecurityConfig.config(http);
        }
        http
                .authorizeRequests()
                .antMatchers("/static-resource/**").permitAll()
                .anyRequest().authenticated()
                .and()
                .exceptionHandling().accessDeniedPage("/WEB-INF/views/common/401.jsp")
                .and()
                .formLogin()
                .usernameParameter("username")
                .passwordParameter("password")
                .permitAll()
                .and()
                .logout()
                .permitAll();
        http.exceptionHandling().authenticationEntryPoint(casAuthenticationEntryPoint())
                .and()
                .addFilter(casAuthenticationFilter())
                .addFilterBefore(casLogoutFilter(), LogoutFilter.class)
                .addFilterBefore(singleSignOutFilter(), CasAuthenticationFilter.class);
        http.csrf().disable();
    }

    @Bean
    public FilterRegistrationBean addMenuLoadFilter() {
        FilterRegistrationBean filterRegistrationBean = new FilterRegistrationBean();
        filterRegistrationBean.setFilter(new Filter() {
            @Override
            public void init(FilterConfig filterConfig) throws ServletException {

            }

            @Override
            public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
                HttpServletRequest httpServletRequest = (HttpServletRequest) request;
                httpServletRequest.setAttribute("menuGroups", SSOUtils.getUserOfLogin().getMenuGroups());
                chain.doFilter(request, response);
            }

            @Override
            public void destroy() {

            }
        });
        filterRegistrationBean.addUrlPatterns("*");
        filterRegistrationBean.setOrder(1);
        return filterRegistrationBean;
    }

    /**
     * 指定service相关信息
     */
    @Bean
    public ServiceProperties serviceProperties() {
        ServiceProperties serviceProperties = new ServiceProperties();
        serviceProperties.setService(casConfig.getAppServerUrl() + casConfig.getAppLoginUrl());
        serviceProperties.setAuthenticateAllArtifacts(true);
        return serviceProperties;
    }


    /**
     * 定义认证用户信息获取来源，密码校验规则等
     */
    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        super.configure(auth);
        auth.authenticationProvider(casAuthenticationProvider());
    }


    /**
     * 认证的入口
     */
    @Bean
    public PrimusCasAuthenticationEntryPoint casAuthenticationEntryPoint() {
        return new PrimusCasAuthenticationEntryPoint(casConfig.getCasServerLoginUrl(), serviceProperties());
    }

    /**
     * CAS认证过滤器
     */
    @Bean
    public PrimusCasAuthenticationFilter casAuthenticationFilter() throws Exception {
        PrimusCasAuthenticationFilter casAuthenticationFilter = new PrimusCasAuthenticationFilter();
        casAuthenticationFilter.setRequiresAuthenticationRequestMatcher(new CasAuthenticationRequestMatcher());
        casAuthenticationFilter.setAuthenticationManager(authenticationManager());
        casAuthenticationFilter.setFilterProcessesUrl(casConfig.getAppLoginUrl());
        return casAuthenticationFilter;
    }

    /**
     * cas 认证 Provider
     */
    @Bean
    public PrimusCasAuthenticationProvider casAuthenticationProvider() {
        PrimusCasAuthenticationProvider casAuthenticationProvider = new PrimusCasAuthenticationProvider();
        casAuthenticationProvider.setAuthenticationUserDetailsService(primusUserDetailsService());
        casAuthenticationProvider.setServiceProperties(serviceProperties());
        casAuthenticationProvider.setTicketValidator(cas20ServiceTicketValidator());
        casAuthenticationProvider.setKey("casAuthenticationProviderKey");
        return casAuthenticationProvider;
    }

    /**
     * 用户自定义的AuthenticationUserDetailsService
     */
    @Bean
    public AuthenticationUserDetailsService<CasAssertionAuthenticationToken> primusUserDetailsService() {
        return new PrimusUserDetailService();
    }

    @Bean
    public Cas20ServiceTicketValidator cas20ServiceTicketValidator() {
        return new Cas20ServiceTicketValidator(casConfig.getCasServerUrl());
    }

    /**
     * 单点登出过滤器
     */
    @Bean
    public SingleSignOutFilter singleSignOutFilter() {
        SingleSignOutFilter singleSignOutFilter = new SingleSignOutFilter();
        singleSignOutFilter.setCasServerUrlPrefix(casConfig.getCasServerUrl());
        singleSignOutFilter.setIgnoreInitConfiguration(true);
        return singleSignOutFilter;
    }

    /**
     * 请求单点退出过滤器
     */
    @Bean
    public LogoutFilter casLogoutFilter() {
        LogoutFilter logoutFilter = new LogoutFilter(casConfig.getCasServerLogoutUrl(), new SecurityContextLogoutHandler());
        logoutFilter.setFilterProcessesUrl(casConfig.getAppLogoutUrl());
        return logoutFilter;
    }

}
